<?php

declare(strict_types=1);

namespace Dongasai\WkProtoPhp\Packets;

use Dongasai\WkProtoPhp\Enums\Action;
use Dongasai\WkProtoPhp\Enums\FrameType;
use Dongasai\WkProtoPhp\Enums\Setting;
use Dongasai\WkProtoPhp\Exception\DecodingException;
use Dongasai\WkProtoPhp\Framer;
use Dongasai\WkProtoPhp\Encoder;
use Dongasai\WkProtoPhp\Decoder;

class SubPacket extends Framer
{
    protected Setting $setting;
    protected string $subNo = '';
    protected string $channelId = '';
    protected int $channelType = 0;
    protected Action $action;
    protected string $param = '';

    public function getFrameType(): int
    {
        return FrameType::SUB->value;
    }

    public function getSetting(): Setting
    {
        return $this->setting;
    }

    public function setSetting(Setting $setting): self
    {
        $this->setting = $setting;
        return $this;
    }

    public function getSubNo(): string
    {
        return $this->subNo;
    }

    public function setSubNo(string $subNo): self
    {
        $this->subNo = $subNo;
        return $this;
    }

    public function getChannelId(): string
    {
        return $this->channelId;
    }

    public function setChannelId(string $channelId): self
    {
        $this->channelId = $channelId;
        return $this;
    }

    public function getChannelType(): int
    {
        return $this->channelType;
    }

    public function setChannelType(int $channelType): self
    {
        $this->channelType = $channelType;
        return $this;
    }

    public function getAction(): Action
    {
        return $this->action;
    }

    public function setAction(Action $action): self
    {
        $this->action = $action;
        return $this;
    }

    public function getParam(): string
    {
        return $this->param;
    }

    public function setParam(string $param): self
    {
        $this->param = $param;
        return $this;
    }

    public static function decodePacket(Framer $framer, string $data): self
    {
        $packet = new self();
        $packet->setFrameType($framer->getFrameType());
        $packet->setRemainingLength($framer->getRemainingLength());
        $packet->setNoPersist($framer->getNoPersist());
        $packet->setRedDot($framer->getRedDot());
        $packet->setSyncOnce($framer->getSyncOnce());
        $packet->setDUP($framer->getDUP());
        
        $decoder = new Decoder($data);
        
        try {
            // 消息设置
            $settingValue = $decoder->uint8();
            $packet->setSetting(new Setting($settingValue));
            
            // 客户端消息编号
            $packet->setSubNo($decoder->string());
            
            // 频道ID
            $packet->setChannelId($decoder->string());
            
            // 频道类型
            $packet->setChannelType($decoder->uint8());
            
            // 动作
            $actionValue = $decoder->uint8();
            $packet->setAction(Action::from($actionValue));
            
            // 参数
            $packet->setParam($decoder->string());
            
        } catch (\Throwable $e) {
            $debugInfo = sprintf(
                '解码失败时偏移量: %d, 剩余数据: %d, 错误: %s',
                $decoder->getOffset(),
                $decoder->len(),
                $e->getMessage()
            );
            throw new DecodingException('解码SubPacket失败: ' . $debugInfo, 0, $e);
        }
        
        return $packet;
    }

    public function encode(): string
    {
        $encoder = new Encoder();
        
        // 消息设置
        $encoder->writeUint8($this->setting->getValue());
        
        // 客户端消息编号
        $encoder->writeString($this->subNo);
        
        // 频道ID
        $encoder->writeString($this->channelId);
        
        // 频道类型
        $encoder->writeUint8($this->channelType);
        
        // 动作
        $encoder->writeUint8($this->action->value);
        
        // 参数
        $encoder->writeString($this->param);
        
        return $encoder->getBuffer();
    }

    public function encodeSize(): int
    {
        $size = 0;
        $size += 1; // Setting
        $size += 2 + strlen($this->subNo); // SubNo
        $size += 2 + strlen($this->channelId); // ChannelId
        $size += 1; // ChannelType
        $size += 1; // Action
        $size += 2 + strlen($this->param); // Param
        return $size;
    }

    /**
     * 获取客户端序列号（向后兼容）
     */
    public function getClientSeq(): string
    {
        return $this->subNo;
    }

    /**
     * 获取消息键（向后兼容）
     */
    public function getMsgKey(): string
    {
        return $this->param;
    }
}